﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OverStore.Configuration.Builder.Fluent;
using System.Data.SqlServerCe;
using OverStore.Runtime.Storage;
using OverStore.Configuration.Components;

namespace OverStore.TutorialAsTest.ReasonToChange
{
    [TestClass]
    public class v2_3_3_0
    {
        public class PlaceType
        {
            public int Type { get; set; }

            public string Name { get; set; }

            public string Description { get; set; }
        }

        private static IPersistenceSessionFactory CreatePlaceTypeConfig(string connectionString)
        {
            return OverStoreFluent.Configure()
                        .LogToConsole()
                        .ConnectToDatabase<SqlCeConnection>(connectionString, ProcessOpenedReadersMode.ForciblyCloseReaders)
                        .MapClassToTable<PlaceType, int>("PlaceType")
                            .MapKey("Type", e => e.Type, (e, id) => e.Type = id, Component.IdentifierGenerator.Seq<PlaceType>(-1, -1))
                            .MapScalarValue<string>("Name", e => e.Name, (e, v) => e.Name = v)
                            .MapNullableScalarValue<string>("Description", e => e.Description, (e, v) => e.Description = v)
                            .ParameterlessReader(true)
                                .ReadCommand()
                                    .UseSqlCommand("select * from PlaceType")
                                .EndCommand()
                                .WithIdentifierProvider(Component.IdentifierProvider.Delegate<int>(r => r.GetInt32("Type")))
                            .EndReader()
                        .EndTableMapping()
                   .End();
        }

        public class Animal
        {
            public int Id { get; set; }

            public string Name { get; set; }

            public bool IsPredator { get; set; }
        }

        public static IPersistenceSessionFactory CreateAnimalConfig(string connectionString)
        {
            return OverStoreFluent.Configure()
                    .LogToConsole()
                    .ConnectToDatabase<SqlCeConnection>(connectionString, ProcessOpenedReadersMode.ForciblyCloseReaders)
                    .MapClassToTable<Animal, int>("Animals") // class, identifier type and table name
                        .MapDbGeneratedKey(
                                "Animal_Id",                                        // Primary key column
                                animal => animal.Id,                                // Get primary key from Animal instance
                                (e, id) => e.Id = id,                               // Apply primary key to Animal instance
                                Component.IdentifierGenerator.Seq<Animal>(-1, -1))  // Sequentially generates temporary primary keys for new Animal objects
                        .MapScalarValue("Name", animal => animal.Name, (animal, name) => animal.Name = name)
                        .MapScalarValue("Is_Predator", animal => animal.IsPredator, (animal, isPredator) => animal.IsPredator = isPredator)
                        .ParameterlessReader(true) // Database query for reader without parameters
                            .ReadCommand()
                                .UseSqlCommand("select * from Animals")
                            .EndCommand()
                            .DelegateIdentifierProvider(r => r.GetInt32("Animal_Id"))
                        .EndReader()
                    .EndTableMapping()
                .End();
        }

        [TestMethod]
        public void SaveOnlyOneEntityWhenTwoAreInSession()
        {
            var config = CreatePlaceTypeConfig(TestHelper.CreateMyMoneyCopyAndBuildConnectionString());
            using (var session = config.CreateSession())
            {
                var purse = session.GetByIdentifier<PlaceType, int>(1);
                var account = session.GetByIdentifier<PlaceType, int>(2);

                purse.Description = "this is a purse";
                account.Description = "this is an account";

                session.Save(purse);
            }

            using (var session = config.CreateSession())
            {
                var purse = session.GetByIdentifier<PlaceType, int>(1);
                var account = session.GetByIdentifier<PlaceType, int>(2);

                Assert.AreEqual("this is a purse", purse.Description);
                Assert.AreNotEqual("this is an account", account.Description);
            }
        }

        [TestMethod]
        public void AccessingBySessionIdOnScopeFailing()
        {
            var config = CreateAnimalConfig(TestHelper.CreateTestDbCopyAndBuildConnectionString());
            using (var session = config.CreateSession())
            {
                var newAnimal = new Animal { Name = "Strange thing" };
                using (var scope = session.OpenScope())
                {
                    session.Add(newAnimal);
                    // Before saving entity is accessbile by identifier generated by session.
                    Assert.AreSame(newAnimal, session.GetByIdentifier<Animal, int>(-2));
                    session.Save(newAnimal);
                    // After saving but before scope completing entity must be accessible by identifier generated by database
                    Assert.AreSame(newAnimal, session.GetByIdentifier<Animal, int>(1));
                }
                // If scope fails, instance must become available by old identifier.
                Assert.AreSame(newAnimal, session.GetByIdentifier<Animal, int>(-2));
            }
        }

        [TestMethod]
        public void AccessingByDatabaseGeneratedIdOnScopeCompleting()
        {
            // Repeat test above, but complete scope this time.
            var config = CreateAnimalConfig(TestHelper.CreateTestDbCopyAndBuildConnectionString());
            using (var session = config.CreateSession())
            {
                var newAnimal = new Animal { Name = "Strange thing" };
                using (var scope = session.OpenScope())
                {
                    session.Add(newAnimal);
                    // Before saving entity is accessbile by identifier generated by session.
                    Assert.AreSame(newAnimal, session.GetByIdentifier<Animal, int>(-2));
                    session.Save(newAnimal);
                    // After saving but before scope completing entity must be accessible by identifier generated by database
                    Assert.AreSame(newAnimal, session.GetByIdentifier<Animal, int>(1));
                    scope.Complete();
                }
                // If scope fails, instance must still be available by identifier generated by database.
                Assert.AreSame(newAnimal, session.GetByIdentifier<Animal, int>(1));
            }
        }
    }
}
